package com.expert.miniapp.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;

/**
 * DifyClient - Dify API统一客户端
 *
 * 支持Dify API的两种响应模式：
 * 1. blocking - 非流式输出（返回完整结果）
 * 2. streaming - 流式输出（SSE格式）
 *
 * @author System
 * @since 2025-11-14
 */
public class DifyClient {

    private static final Logger logger = LoggerFactory.getLogger(DifyClient.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * OpenAI格式调用（用于/chat/completions端点）
     * content字段序列化为JSON字符串（Dify要求）
     * 数组字段自动转换为逗号分隔字符串
     *
     * @param url API端点URL
     * @param apiKey API Key
     * @param content message的content字段（将序列化为JSON字符串）
     * @param model 模型名称
     * @param stream 是否流式输出
     * @param timeoutMillis 超时时间（毫秒）
     * @return AI返回的文本内容
     * @throws Exception 调用失败时抛出异常
     */
    public static String callOpenAIFormat(
            String url,
            String apiKey,
            Map<String, Object> content,
            String model,
            boolean stream,
            int timeoutMillis
    ) throws Exception {
        logger.info("调用OpenAI格式API: url={}, model={}, stream={}", url, model, stream);

        // 转换数组字段为逗号分隔字符串
        Map<String, Object> convertedContent = convertArraysToStrings(content);

        // 将content对象序列化为JSON字符串
        String contentJson = objectMapper.writeValueAsString(convertedContent);
        logger.debug("Content JSON字符串长度: {}", contentJson.length());

        // 构造OpenAI格式请求体
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("model", model);

        Map<String, Object> message = new LinkedHashMap<>();
        message.put("role", "user");
        message.put("content", contentJson);  // content是JSON字符串
        requestBody.put("messages", new Object[]{message});
        requestBody.put("stream", stream);

        String requestJson = objectMapper.writeValueAsString(requestBody);
        logger.debug("OpenAI请求体: {}", requestJson);

        // 创建HTTP客户端
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofMillis(timeoutMillis))
                .build();

        // 创建请求
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/json; charset=UTF-8")
                .header("Authorization", "Bearer " + apiKey)
                .POST(HttpRequest.BodyPublishers.ofString(requestJson, StandardCharsets.UTF_8))
                .timeout(Duration.ofMillis(timeoutMillis))
                .build();

        // 发送请求
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));

        logger.info("OpenAI API响应状态码: {}", response.statusCode());

        if (response.statusCode() != 200) {
            String errorMsg = String.format("API调用失败: HTTP %d, 响应: %s",
                    response.statusCode(), response.body());
            logger.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }

        // 解析OpenAI格式响应
        JsonNode root = objectMapper.readTree(response.body());
        logger.debug("OpenAI响应: {}", root.toPrettyString());

        // 检查错误
        if (root.has("error")) {
            String errorMsg = root.path("error").path("message").asText("未知错误");
            logger.error("API返回错误: {}", errorMsg);
            throw new RuntimeException("API错误: " + errorMsg);
        }

        // 提取回复内容 - OpenAI格式: choices[0].message.content
        JsonNode choices = root.path("choices");
        if (choices.isArray() && choices.size() > 0) {
            String responseContent = choices.get(0).path("message").path("content").asText();
            if (responseContent != null && !responseContent.isEmpty()) {
                logger.info("API返回内容长度: {}", responseContent.length());
                return responseContent;
            }
        }

        throw new RuntimeException("API响应中未找到有效内容");
    }

    /**
     * 将Map中的List类型值转换为逗号分隔字符串
     */
    private static Map<String, Object> convertArraysToStrings(Map<String, Object> content) {
        Map<String, Object> converted = new LinkedHashMap<>();
        for (Map.Entry<String, Object> entry : content.entrySet()) {
            Object value = entry.getValue();
            if (value instanceof java.util.List) {
                // 将List转换为逗号分隔字符串
                java.util.List<?> list = (java.util.List<?>) value;
                String joined = list.stream()
                        .map(Object::toString)
                        .collect(java.util.stream.Collectors.joining(","));
                converted.put(entry.getKey(), joined);
            } else {
                converted.put(entry.getKey(), value);
            }
        }
        return converted;
    }

    /**
     * OpenAI格式调用（流式输出，SSE格式）
     * content字段序列化为JSON字符串（Dify要求）
     * 数组字段自动转换为逗号分隔字符串
     *
     * @param url API端点URL
     * @param apiKey API Key
     * @param content message的content字段（将序列化为JSON字符串）
     * @param model 模型名称
     * @param timeoutMillis 超时时间（毫秒）
     * @param chunkConsumer 流式数据回调（可选）
     * @return 完整的AI响应内容
     * @throws Exception 调用失败时抛出异常
     */
    public static String callOpenAIFormatStreaming(
            String url,
            String apiKey,
            Map<String, Object> content,
            String model,
            int timeoutMillis,
            Consumer<String> chunkConsumer
    ) throws Exception {
        logger.info("调用OpenAI格式API（流式）: url={}, model={}", url, model);

        // 转换数组字段为逗号分隔字符串
        Map<String, Object> convertedContent = convertArraysToStrings(content);

        // 将content对象序列化为JSON字符串
        String contentJson = objectMapper.writeValueAsString(convertedContent);
        logger.debug("Content JSON字符串长度: {}", contentJson.length());

        // 构造OpenAI格式请求体（stream=true）
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("model", model);

        Map<String, Object> message = new LinkedHashMap<>();
        message.put("role", "user");
        message.put("content", contentJson);  // content是JSON字符串
        requestBody.put("messages", new Object[]{message});
        requestBody.put("stream", true);  // 启用流式输出

        String requestJson = objectMapper.writeValueAsString(requestBody);
        logger.debug("OpenAI流式请求体: {}", requestJson);

        // 创建HTTP客户端
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofMillis(timeoutMillis))
                .build();

        // 创建请求
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/json; charset=UTF-8")
                .header("Authorization", "Bearer " + apiKey)
                .POST(HttpRequest.BodyPublishers.ofString(requestJson, StandardCharsets.UTF_8))
                .timeout(Duration.ofMillis(timeoutMillis))
                .build();

        // 发送请求并处理SSE流
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));

        logger.info("OpenAI流式API响应状态码: {}", response.statusCode());

        if (response.statusCode() != 200) {
            String errorMsg = String.format("API调用失败: HTTP %d, 响应: %s",
                    response.statusCode(), response.body());
            logger.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }

        // 解析SSE流数据
        StringBuilder fullContent = new StringBuilder();
        String responseBody = response.body();
        String[] lines = responseBody.split("\n");

        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty() || line.equals("data: [DONE]")) {
                continue;
            }

            // SSE格式：data: {...}
            if (line.startsWith("data: ")) {
                String jsonData = line.substring(6).trim();
                if (jsonData.isEmpty()) continue;

                try {
                    JsonNode chunk = objectMapper.readTree(jsonData);

                    // 检查错误
                    if (chunk.has("error")) {
                        String errorMsg = chunk.path("error").path("message").asText("未知错误");
                        logger.error("流式输出错误: {}", errorMsg);
                        throw new RuntimeException("API错误: " + errorMsg);
                    }

                    // 提取delta内容 - OpenAI流式格式: choices[0].delta.content
                    JsonNode choices = chunk.path("choices");
                    if (choices.isArray() && choices.size() > 0) {
                        JsonNode delta = choices.get(0).path("delta");
                        if (delta.has("content")) {
                            String deltaContent = delta.path("content").asText();
                            if (deltaContent != null && !deltaContent.isEmpty()) {
                                fullContent.append(deltaContent);
                                if (chunkConsumer != null) {
                                    chunkConsumer.accept(deltaContent);
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.warn("解析SSE数据块失败: {}", e.getMessage());
                }
            }
        }

        String result = fullContent.toString();
        logger.info("OpenAI流式输出完成，总长度: {}", result.length());

        if (result.isEmpty()) {
            throw new RuntimeException("流式输出未返回任何内容");
        }

        return result;
    }

    /**
     * Blocking模式调用Dify API（非流式）
     *
     * @param url Dify API端点URL
     * @param apiKey Dify API Key
     * @param inputs 自定义输入变量（对应Dify应用中定义的变量）
     * @param query 用户查询内容（可选，如果inputs已包含所有信息可为空）
     * @param userId 用户标识
     * @param timeoutMillis 超时时间（毫秒）
     * @return Dify返回的answer字段内容
     * @throws Exception 调用失败时抛出异常
     */
    public static String callBlocking(
            String url,
            String apiKey,
            Map<String, Object> inputs,
            String query,
            String userId,
            int timeoutMillis
    ) throws Exception {
        logger.info("调用Dify API (blocking): url={}", url);

        // 构造请求体
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("inputs", inputs);
        requestBody.put("query", query != null ? query : "");
        requestBody.put("response_mode", "blocking");
        requestBody.put("user", userId);

        String requestJson = objectMapper.writeValueAsString(requestBody);
        logger.debug("请求体: {}", requestJson);

        // 创建HTTP客户端
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofMillis(timeoutMillis))
                .build();

        // 创建请求
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/json; charset=UTF-8")
                .header("Authorization", "Bearer " + apiKey)
                .POST(HttpRequest.BodyPublishers.ofString(requestJson, StandardCharsets.UTF_8))
                .timeout(Duration.ofMillis(timeoutMillis))
                .build();

        // 发送请求
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));

        logger.info("Dify响应状态码: {}", response.statusCode());

        if (response.statusCode() != 200) {
            String errorMsg = String.format("Dify API调用失败: HTTP %d, 响应: %s",
                    response.statusCode(), response.body());
            logger.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }

        // 解析响应
        JsonNode root = objectMapper.readTree(response.body());
        logger.debug("Dify响应: {}", root.toPrettyString());

        // 检查错误
        if (root.has("code") && !"success".equalsIgnoreCase(root.path("code").asText())) {
            String errorMsg = root.path("message").asText("未知错误");
            logger.error("Dify返回错误: {}", errorMsg);
            throw new RuntimeException("Dify错误: " + errorMsg);
        }

        // 提取answer字段
        String answer = root.path("answer").asText("");
        logger.info("Dify返回内容长度: {} 字符", answer.length());

        return answer;
    }

    /**
     * Streaming模式调用Dify API（流式输出/SSE）
     *
     * @param url Dify API端点URL
     * @param apiKey Dify API Key
     * @param inputs 自定义输入变量
     * @param query 用户查询内容
     * @param userId 用户标识
     * @param timeoutMillis 超时时间（毫秒）
     * @param chunkConsumer 流式数据消费者（每收到一个chunk调用一次）
     * @return 完整的累积结果
     * @throws Exception 调用失败时抛出异常
     */
    public static String callStreaming(
            String url,
            String apiKey,
            Map<String, Object> inputs,
            String query,
            String userId,
            int timeoutMillis,
            Consumer<String> chunkConsumer
    ) throws Exception {
        logger.info("调用Dify API (streaming): url={}", url);

        // 构造请求体
        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("inputs", inputs);
        requestBody.put("query", query != null ? query : "");
        requestBody.put("response_mode", "streaming");
        requestBody.put("user", userId);

        String requestJson = objectMapper.writeValueAsString(requestBody);
        logger.debug("请求体: {}", requestJson);

        // 创建HTTP客户端
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofMillis(timeoutMillis))
                .build();

        // 创建请求
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/json; charset=UTF-8")
                .header("Authorization", "Bearer " + apiKey)
                .POST(HttpRequest.BodyPublishers.ofString(requestJson, StandardCharsets.UTF_8))
                .timeout(Duration.ofMillis(timeoutMillis))
                .build();

        // 发送请求，接收流式响应
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));

        logger.info("Dify响应状态码: {}", response.statusCode());

        if (response.statusCode() != 200) {
            String errorMsg = String.format("Dify API调用失败: HTTP %d, 响应: %s",
                    response.statusCode(), response.body());
            logger.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }

        // 处理SSE流式响应
        StringBuilder fullContent = new StringBuilder();
        String[] lines = response.body().split("\n");

        for (String line : lines) {
            if (line.startsWith("data: ")) {
                String data = line.substring(6).trim();

                // SSE结束标记
                if ("[DONE]".equals(data)) {
                    logger.debug("SSE流结束");
                    break;
                }

                try {
                    JsonNode eventData = objectMapper.readTree(data);
                    String event = eventData.path("event").asText("");

                    // Dify的message事件包含answer字段
                    if ("message".equals(event)) {
                        String chunk = eventData.path("answer").asText("");
                        if (!chunk.isEmpty()) {
                            fullContent.append(chunk);
                            if (chunkConsumer != null) {
                                chunkConsumer.accept(chunk);
                            }
                        }
                    } else if ("error".equals(event)) {
                        String errorMsg = eventData.path("message").asText("流式传输错误");
                        logger.error("Dify流式错误: {}", errorMsg);
                        throw new RuntimeException("Dify流式错误: " + errorMsg);
                    }
                } catch (Exception e) {
                    logger.warn("解析SSE数据失败: {}", data, e);
                }
            }
        }

        logger.info("Dify流式输出完成，总长度: {} 字符", fullContent.length());
        return fullContent.toString();
    }

    /**
     * Streaming模式调用Dify API（简化版，不需要chunk消费者）
     */
    public static String callStreaming(
            String url,
            String apiKey,
            Map<String, Object> inputs,
            String query,
            String userId,
            int timeoutMillis
    ) throws Exception {
        return callStreaming(url, apiKey, inputs, query, userId, timeoutMillis, null);
    }
}
